//
// Created by wang on 2021/7/4.
//

#include "sys.h"
#include <sys/types.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <ifaddrs.h>
#include "eth.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


static int32_t get_device_mac(
        mac mac_buf, const char *dev_name);

uint32_t pil_getpid(void) {
    return getpid();
}

ret_t pil_device_mac(mac mac_buf) {
    struct ifaddrs *ifap, *p;
    int32_t ret = ENET_RET_SUCCESS;

    if(mac_buf == NULL)
        return ERR_PIL_INVALID_PARAMETERS;

    if (getifaddrs(&ifap) < 0) {
        return ERR_PIL_GET_DEVICE_MAC_FAIL ;
    }

    for (p = ifap; p != NULL; p = p->ifa_next) {

        if(!get_device_mac(mac_buf, p->ifa_name) && !ETH_IS_ZERO_MAC(mac_buf))
            goto end;
    }

    ret = ERR_PIL_NO_MAC_IN_DEVICE_LIST;

    end:
    freeifaddrs(ifap);
    return ret;
}

/* TODO: simplify return value */
/* mac_buf: `out`
 * dev_name: `in` */
static int32_t get_device_mac(mac mac_buf,const char *dev_name) {

    int ret = 0, i = 0;
    int fd = -1;

    struct ifreq ifr;

    if (NULL == mac_buf || NULL == dev_name) {
        return -1;
    }

    /* if dev name is too long, just report error. */
    if (strlen(dev_name) > IFNAMSIZ) {
        return -2;
    }

    strcpy(ifr.ifr_name, dev_name);
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        return -3;
    }

    if (ioctl(fd, SIOCGIFHWADDR, &ifr, sizeof(ifr)) < 0) {
        ret = -4;
        goto end;
    }

    memcpy(mac_buf, (byte *)&(ifr.ifr_hwaddr.sa_data[i]), MAC_LEN);

    end:
    close(fd);
    return ret;
}

/* On linux, you can control pipe mode with fcntl */
ret_t pil_create_pipe(
        const char *name, uint8_t perm, uint32_t mode, char *pipe_desc) {

    uint8_t m = 0;

    if (perm & PIL_PIPE_R) {
        m |= (S_IRUSR | S_IRGRP);
    }
    if (perm & PIL_PIPE_W) {
        m |= (S_IWUSR | S_IWGRP);

    }

    if (mkfifo(name, m) < 0) {
        return ERR_PIL_CREATE_PIPE_FAILED;
    }

    strcpy(pipe_desc, name);
    return ENET_RET_SUCCESS;
}

ret_t pil_connect_pipe(
        const char * name, uint8_t perm,  pil_pipe_handle *handle) {

    int p = 0;
    int file = -1;

    if (perm & PIL_PIPE_R)
        p |= O_RDONLY;
    if (perm & PIL_PIPE_W)
        p |= O_WRONLY;

    file = open(name, p);
    if (file < 0) {
        if (errno == ENOENT) {
            return ERR_PIL_PIPE_NOT_FOUND;
        } else {
            return ERR_PIL_CONNECT_PIPE_FAILED;
        }
    }

    handle->handle = file;

    return ENET_RET_SUCCESS;
}

ret_t pil_read_pipe(pil_pipe_handle *handle, byte *buf, uint32_t len,
                       unsigned long *read_len) {

    ssize_t rd;

    if (len == 0) {
        return ENET_RET_SUCCESS;
    }

    rd = read(handle->handle, buf, len);
    if (rd < 0) {
        return ERR_PIL_READ_PIPE_FAILED;
    }

    *read_len = rd;

    return ENET_RET_SUCCESS;
}

ret_t pil_write_pipe(pil_pipe_handle *handle, byte *buf, uint32_t len,
                        unsigned long *write_len) {

    ssize_t wt;

    if (len == 0) {
        return ENET_RET_SUCCESS;
    }

    wt = write(handle->handle, buf, len);
    if (wt < 0) {
        return ERR_PIL_WRITE_PIPE_FAILED;
    }

    *write_len = wt;
    return ENET_RET_SUCCESS;
}

void pil_sleep(uint32_t milliseconds) {
    usleep(milliseconds * 1000);
}
